#include "COperLogManager.h"

#include <random>
#include <iostream>
#include <sstream>
#include <iomanip>

#include <QDateTime>
#include <QSqlQuery>
#include <QVariant>
#include "glog/logging.h"

#include "CDBConnector.h"

using namespace zl;

#define CURRENT_DATESTR QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss").toStdString()

COperLogManager* COperLogManager::Instance_ = nullptr;
CabinetInfo COperLogManager::Config_;
std::string COperLogManager::Cabinet_No_ = "";

zl::COperLogManager *zl::COperLogManager::Instance()
{
    if (Instance_ == nullptr)
    {
        Instance_ = new COperLogManager;
    }

    return Instance_;
}

void COperLogManager::Release()
{
    LOG(WARNING) << "release operator log manager...";
    delete Instance_;
    Instance_ = nullptr;
}

std::string COperLogManager::GeneratedLogID()
{
    std::default_random_engine e;
    e.seed(QDateTime::currentDateTime().toMSecsSinceEpoch());

    std::ostringstream oss;
    oss << Cabinet_No_;
    oss << QDateTime::currentDateTime().toString("yyyyMMddhhmmss").toStdString();
    oss << std::setw(10) << std::setfill('0') << e();

    return oss.str();
}

int32_t COperLogManager::Init(const std::string &cabinetno, CabinetInfo config)
{
    Config_ = config;
    Cabinet_No_ = cabinetno;
    return EResult_Success;
}

OperLog COperLogManager::CreateOperLog()
{
    LOG(INFO)<<"Create Operator Log";
    OperLog operlog;
    operlog.log_id = GeneratedLogID();
    operlog.cabinet_no = Cabinet_No_;
    operlog.box_no = 0;
    operlog.action = "";
    operlog.type = "";
    operlog.account = "";
    operlog.time = "";
    operlog.confirmed = 0;

    return operlog;
}

int32_t COperLogManager::CommitOperLog(OperLog &operlog)
{
    LOG(INFO) << "Commit Operator Log: " << operlog.log_id;
    int32_t tmp = 0;

    QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
    query.prepare("INSERT INTO T_OPER_LOG (LOG_ID, CABINET_NO, BOX_NO,\
        ACTION, TYPE, ACCOUNT, TIME, CONFIRMED) VALUES(:log_id, :cabinet_no, :box_no, \
        :action, :type, :account, :time, :confirmed)");
    query.bindValue(":log_id", operlog.log_id.c_str());
    query.bindValue(":cabinet_no", operlog.cabinet_no.c_str());
    query.bindValue(":box_no", operlog.box_no);
    query.bindValue(":action", operlog.action.c_str());
    query.bindValue(":type", operlog.type.c_str());
    query.bindValue(":account", operlog.account.c_str());
    query.bindValue(":time", CURRENT_DATESTR.c_str());
    query.bindValue(":confirmed", tmp);

    int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
    if (ret != zl::CDBConnector::EDBError_Success)
    {
        LOG(ERROR) << "Commit Operator Log Failed: " << ret;
        return EResult_Failed;
    }

    LOG(INFO) << "begin order success: " << ret;
    return EResult_Success;
}

int32_t COperLogManager::GetAllOperLogs(OperLogVec &vec, const std::string &begin_time, const std::string &end_time, const std::string &phone, const int32_t lockerno)
{
    LOG(INFO) << "query order";
    QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
    QString str = "SELECT * FROM T_OPER_LOG WHERE TIME>=:begin_time AND TIME<=:end_time";
    if (!phone.empty())
    {
        str += " AND ACCOUNT=:phone";
    }
    if (lockerno > 0)
    {
        str += " AND BOX_NO=:box_no";
    }
    str += " ORDER BY TIME DESC";

    query.prepare(str);
    query.bindValue(":begin_time", begin_time.c_str());
    query.bindValue(":end_time", end_time.c_str());
    query.bindValue(":phone", phone.c_str());
    query.bindValue(":box_no", lockerno);

    int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
    if (ret != zl::CDBConnector::EDBError_Success)
    {
        LOG(ERROR) << "get all oper log failed: " << ret;
        return EResult_Failed;
    }

    while (query.next())
    {
        OperLog log;
        log.log_id = query.value("LOG_ID").toString().toStdString();
        log.time = query.value("TIME").toString().toStdString();
        log.box_no = query.value("BOX_NO").toInt();
        log.type = query.value("TYPE").toString().toStdString();
        log.action = query.value("ACTION").toString().toStdString();
        log.account = query.value("ACCOUNT").toString().toStdString();
        log.confirmed = query.value("CONFIRMED").toInt();
        vec.push_back(log);
    }

    LOG(INFO) << "get all oper log success: " << vec.size();
    return EResult_Success;
}

int32_t COperLogManager::ConfirmOperLog(const std::string &log_id)
{
    LOG(INFO) << "confirm oper log:" << log_id;
    QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
    query.prepare("UPDATE T_OPER_LOG SET CONFIRMED=? WHERE LOG_ID = ?");
    query.bindValue(0, 1);
    query.bindValue(1, log_id.c_str());
    auto ret = zl::CDBConnector::Instance()->ExecQuery(query);
    if (ret != zl::CDBConnector::EDBError_Success)
    {
        LOG(ERROR) << "confirm oper log failed:" << ret;
        return zl::EResult_Failed;
    }

    LOG(INFO) << "confirm oper log success";
    return zl::EResult_Success;
}

int32_t COperLogManager::GetAllUnconfirmedOperLog(OperLogVec &operlog)
{
    QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
    query.prepare("SELECT * FROM T_OPER_LOG WHERE CONFIRMED=?");
    query.bindValue(0, 0);
    auto ret = zl::CDBConnector::Instance()->ExecQuery(query);
    if (ret != zl::CDBConnector::EDBError_Success)
    {
        LOG(ERROR) << "retrieve failed:" << ret;
        return zl::EResult_Failed;
    }

    operlog.clear();
    while (query.next())
    {
        zl::OperLog log;
        log.log_id = query.value("LOG_ID").toString().toStdString();
        log.cabinet_no = query.value("CABINET_NO").toString().toStdString();
        log.box_no = query.value("BOX_NO").toInt();
        log.action = query.value("ACTION").toString().toStdString();
        log.type = query.value("TYPE").toString().toStdString();
        log.account = query.value("ACCOUNT").toString().toStdString();
        log.time = query.value("TIME").toDateTime().toString("yyyy-MM-dd hh:mm:ss").toStdString();
        log.confirmed = query.value("CONFIRMED").toInt();

        operlog.push_back(log);
    }
    return zl::EResult_Success;
}
